Skip to content

fix: return optional total balance in list apps response for subwallets#2057

Open
im-adithya wants to merge 2 commits intomasterfrom
fix/subwallet-balance
Open

fix: return optional total balance in list apps response for subwallets#2057
im-adithya wants to merge 2 commits intomasterfrom
fix/subwallet-balance

Conversation

@im-adithya
Copy link
Member

@im-adithya im-adithya commented Feb 11, 2026

Fixes #2031

Summary by CodeRabbit

  • New Features

    • Subwallet lists now show an aggregated total balance returned from the server.
    • List responses include a per-list totalBalance used by the UI for totals and pagination.
  • Bug Fixes / Accuracy

    • Balance calculation improved to account for incoming, outgoing, fees and reserves across settled and pending transactions.
  • Tests

    • Added tests validating the total subwallet balance calculation.

@im-adithya im-adithya requested a review from rolznz February 11, 2026 18:52
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds total subwallet balance computation and returns it in the ListApps API response. Subwallet filtering logic was refactored to handle tri-state filtering. Frontend updated to request subwallets and consume totalCount and totalBalance.

Changes

Cohort / File(s) Summary
API: endpoint & models
api/api.go, api/models.go
Refactored SubWallets filtering to tri-state (true/false/nil); ListApps response extended with TotalBalance *int64; ListApps invokes balance computation when subWallets=true.
DB: queries & tests
db/queries/get_total_subwallet_balance.go, db/queries/get_total_subwallet_balance_test.go
New GetTotalSubwalletBalance(tx *gorm.DB) (int64, error) computes net subwallet balance by summing settled INCOMING and subtracting OUTGOING (including fees and reserves, settled or pending); added unit test validating calculation.
Frontend: types & UI
frontend/src/types.ts, frontend/src/screens/subwallets/SubwalletList.tsx
Added optional totalBalance to ListAppsResponse; SubwalletList now requests subWallets: true and uses totalCount and totalBalance from the response instead of deriving from the apps array.

Sequence Diagram

sequenceDiagram
    participant FE as Frontend (SubwalletList)
    participant API as API (ListApps)
    participant DB as Database (Queries)

    FE->>API: ListApps(subWallets: true)
    API->>API: Apply SubWallets tri-state filtering
    API->>DB: GetTotalSubwalletBalance(tx)
    DB->>DB: Select subwallet app IDs (metadata.app_store_app_id)
    DB->>DB: SUM INCOMING where state=SETTLED
    DB->>DB: SUM OUTGOING (amount+fee+reserve) where state IN (SETTLED,PENDING)
    DB-->>API: Return net balance (received - spent)
    API-->>FE: ListAppsResponse { Apps, TotalCount, TotalBalance }
    FE->>FE: Render list and display totalCount & totalBalance
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through rows and sums with care,

Counting coins tucked safely there,
Subwallet totals now leap into sight,
No more missing cents in the night,
A little hop — the balances unite.

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR adds totalBalance calculation and filtering logic for subwallets, but the linked issue #2031 specifically reports that subwallet counts are incorrect on pagination. The totalBalance addition doesn't directly address the reported pagination/count problem. Verify whether the fix properly handles pagination for subwallet counts by ensuring totalCount reflects all subwallets across pages, not just the current page subset.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: return optional total balance in list apps response for subwallets' clearly and specifically describes the main change: adding an optional total balance field to the ListAppsResponse for subwallet queries.
Out of Scope Changes check ✅ Passed All changes are directly related to subwallet balance calculation and filtering: backend computation of subwallet totals, database queries, API response extensions, and frontend consumption of the new totalBalance field.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/subwallet-balance

No actionable comments were generated in the recent review. 🎉


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@api/api.go`:
- Around line 530-535: The call to queries.GetTotalSubwalletBalance currently
assumes a single int64 return and masks DB errors by setting totalBalance to 0;
change the call-site in the block that checks filters.SubWallets to handle the
new signature (int64, error) from GetTotalSubwalletBalance: call
totalBalanceMsat, err := queries.GetTotalSubwalletBalance(api.db), on error log
the error via the existing logger and return the error (or otherwise degrade by
leaving totalBalance nil), and only set totalBalance = &totalBalanceMsat when
err == nil; update references to totalBalance accordingly to avoid exposing a
zero balance on DB failures.

In `@db/queries/get_total_subwallet_balance.go`:
- Around line 10-33: GetTotalSubwalletBalance currently ignores database errors
from the two Scan calls (Scan(&received) and Scan(&spent)), so change its
signature to return (int64, error), capture the returned *gorm.DB from each Scan
(e.g., res := tx.Table(...).Scan(&received)), check res.Error and return 0, err
on failure, and only compute and return received.Sum - spent.Sum with nil error
when both queries succeed; update the caller in api/api.go to handle the new
(int64, error) result accordingly.
🧹 Nitpick comments (1)
db/queries/get_total_subwallet_balance_test.go (1)

25-59: Consider checking errors from svc.DB.Save() calls.

Multiple svc.DB.Save(...) calls (Lines 25, 31, 39, 49, 59) don't assert on the returned error. A silent save failure would cause a misleading test result. Using require.NoError(t, svc.DB.Save(...).Error) would make failures easier to diagnose.

Comment on lines 10 to 33
func GetTotalSubwalletBalance(tx *gorm.DB) int64 {
subwalletAppIDsQuery := tx.Model(&db.App{}).
Select("id").
Where(datatypes.JSONQuery("metadata").Equals(constants.SUBWALLET_APPSTORE_APP_ID, "app_store_app_id"))

var received struct {
Sum int64
}
tx.
Table("transactions").
Select("SUM(amount_msat) as sum").
Where("app_id IN (?) AND type = ? AND state = ?", subwalletAppIDsQuery, constants.TRANSACTION_TYPE_INCOMING, constants.TRANSACTION_STATE_SETTLED).
Scan(&received)

var spent struct {
Sum int64
}
tx.
Table("transactions").
Select("SUM(amount_msat + fee_msat + fee_reserve_msat) as sum").
Where("app_id IN (?) AND type = ? AND (state = ? OR state = ?)", subwalletAppIDsQuery, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING).
Scan(&spent)

return received.Sum - spent.Sum
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing error handling on DB queries.

Both Scan(&received) (Line 22) and Scan(&spent) (Line 31) ignore the returned *gorm.DB result, so any query failure is silently swallowed — the function would return 0 (or a partial result) without the caller knowing. Consider returning an (int64, error) tuple, or at minimum logging errors.

Proposed fix: return errors to caller
-func GetTotalSubwalletBalance(tx *gorm.DB) int64 {
+func GetTotalSubwalletBalance(tx *gorm.DB) (int64, error) {
 	subwalletAppIDsQuery := tx.Model(&db.App{}).
 		Select("id").
 		Where(datatypes.JSONQuery("metadata").Equals(constants.SUBWALLET_APPSTORE_APP_ID, "app_store_app_id"))
 
 	var received struct {
 		Sum int64
 	}
-	tx.
+	if err := tx.
 		Table("transactions").
 		Select("SUM(amount_msat) as sum").
 		Where("app_id IN (?) AND type = ? AND state = ?", subwalletAppIDsQuery, constants.TRANSACTION_TYPE_INCOMING, constants.TRANSACTION_STATE_SETTLED).
-		Scan(&received)
+		Scan(&received).Error; err != nil {
+		return 0, err
+	}
 
 	var spent struct {
 		Sum int64
 	}
-	tx.
+	if err := tx.
 		Table("transactions").
 		Select("SUM(amount_msat + fee_msat + fee_reserve_msat) as sum").
 		Where("app_id IN (?) AND type = ? AND (state = ? OR state = ?)", subwalletAppIDsQuery, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING).
-		Scan(&spent)
+		Scan(&spent).Error; err != nil {
+		return 0, err
+	}
 
-	return received.Sum - spent.Sum
+	return received.Sum - spent.Sum, nil
 }

The caller in api/api.go (Lines 532-535) would need a corresponding update to handle the error.

🤖 Prompt for AI Agents
In `@db/queries/get_total_subwallet_balance.go` around lines 10 - 33,
GetTotalSubwalletBalance currently ignores database errors from the two Scan
calls (Scan(&received) and Scan(&spent)), so change its signature to return
(int64, error), capture the returned *gorm.DB from each Scan (e.g., res :=
tx.Table(...).Scan(&received)), check res.Error and return 0, err on failure,
and only compute and return received.Sum - spent.Sum with nil error when both
queries succeed; update the caller in api/api.go to handle the new (int64,
error) result accordingly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rolznz we don't do this for any query in db/queries, I think this should be fine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@im-adithya hmm, I think we should add it actually, especially for the other queries - that's my fault for not adding it. I'll add an issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added here, will open a new PR to add it on other db queries too 👌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Subwallet page only shows count of wallets on first page

2 participants

Comments